#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <nxos.h>

#include <InfoNES.h>
#include <InfoNES_System.h>
#include <InfoNES_pAPU.h>

#define USE_NX_WINDOW 1
#define USE_FULLSCREEN 1 
// #define USE_SOUND 1
// #define USE_HW_ACCEL 1

#ifdef USE_SOUND
#define SOUND_DEVICE "sb16"

#endif /* USE_SOUND */

#ifdef USE_NX_WINDOW
#include <wm/window.h>
#endif /* USE_NX_WINDOW */

#define JOYSTICK_LEFT   NX_KEY_LEFT
#define JOYSTICK_RIGHT  NX_KEY_RIGHT
#define JOYSTICK_UP     NX_KEY_UP
#define JOYSTICK_DOWN   NX_KEY_DOWN
#define JOYSTICK_A      NX_KEY_A
#define JOYSTICK_B      NX_KEY_B
#define JOYSTICK_a      NX_KEY_a
#define JOYSTICK_b      NX_KEY_b
#define JOYSTICK_X      NX_KEY_X
#define JOYSTICK_Y      NX_KEY_Y
#define JOYSTICK_x      NX_KEY_x
#define JOYSTICK_y      NX_KEY_y
#define JOYSTICK_SELECT NX_KEY_BACKSPACE
#define JOYSTICK_START  NX_KEY_ENTER
#define JOYSTICK_HOME   NX_KEY_ESCAPE
#define JOYSTICK_CUT    NX_KEY_C
#define JOYSTICK_cut    NX_KEY_c
#define JOYSTICK_SAVE   NX_KEY_V
#define JOYSTICK_save   NX_KEY_v

void start_application( char *filename );
int InfoNES_Load( const char *pszFileName );
void InfoNES_Main();
/*-------------------------------------------------------------------*/
/*  ROM image file information                                       */
/*-------------------------------------------------------------------*/

char szRomName[ 256 ];
char szSaveName[ 256 ];
int nSRAM_SaveFlag;


/* Pad state */
DWORD dwKeyPad1;
DWORD dwKeyPad2;
DWORD dwKeySystem;

int LoadSRAM();
int SaveSRAM();

/* Palette data */
WORD NesPalette[ 64 ] =
{
  0x39ce, 0x1071, 0x0015, 0x2013, 0x440e, 0x5402, 0x5000, 0x3c20,
  0x20a0, 0x0100, 0x0140, 0x00e2, 0x0ceb, 0x0000, 0x0000, 0x0000,
  0x5ef7, 0x01dd, 0x10fd, 0x401e, 0x5c17, 0x700b, 0x6ca0, 0x6521,
  0x45c0, 0x0240, 0x02a0, 0x0247, 0x0211, 0x0000, 0x0000, 0x0000,
  0x7fff, 0x1eff, 0x2e5f, 0x223f, 0x79ff, 0x7dd6, 0x7dcc, 0x7e67,
  0x7ae7, 0x4342, 0x2769, 0x2ff3, 0x03bb, 0x0000, 0x0000, 0x0000,
  0x7fff, 0x579f, 0x635f, 0x6b3f, 0x7f1f, 0x7f1b, 0x7ef6, 0x7f75,
  0x7f94, 0x73f4, 0x57d7, 0x5bf9, 0x4ffe, 0x0000, 0x0000, 0x0000
};

/* For Sound Emulation */
#ifdef USE_SOUND
short final_wave[2048];
int sound_fd;
#endif

#ifdef USE_NX_WINDOW
NX_Window * win;
NX_Surface * surface;
#ifdef USE_FULLSCREEN
NX_Surface * surfaceOrigin;
#endif
#else
NX_Solt fb_solt;
NX_Solt key_solt;
NX_FramebufferInfo fbInfo;
void * fb_buf;
#endif /* USE_NX_WINDOW */

/* 绘制缓冲区 */
// DWORD graphBuffer[NES_DISP_WIDTH*NES_DISP_HEIGHT];

#define DEF_NES_FILE    "/Users/Share/mario.nes"

static void print_keymap(void)
{
  printf("keymap:\n");
  printf("left: <-\nright: ->\nup: ^\ndown: v\n");
  printf("A: a/A\n");
  printf("B: b/B\n");
  printf("select: backspace\n");
  printf("start: enter\n");
  printf("home: escape\n");
}

int main(int argc, char **argv)
{  
  /*-------------------------------------------------------------------*/
  /*  Pad Control                                                      */
  /*-------------------------------------------------------------------*/

  /* Initialize a pad state */
  dwKeyPad1   = 0;
  dwKeyPad2   = 0;
  dwKeySystem = 0;
  char *game = DEF_NES_FILE;
  /* If a rom name specified, start it */
  if ( argc >= 2 )
  {
    //NX_Printf("infones: arg %s\n", argv[ 1 ]);
    game = argv[ 1 ];
  }
  start_application(game);
	return 0;	
}

void exit_application()
{
    InfoNES_Fin(); /* 结束nes */
    NX_WindowDestroy(win);
    NX_WindowExit();
    exit(-1);   /* 退出进程 */
}

/*===================================================================*/
/*                                                                   */
/*     start_application() : Start NES Hardware                      */
/*                                                                   */
/*===================================================================*/
void start_application( char *filename )
{
    
  /* Set a ROM image name */
  strcpy( szRomName, filename );

  //NX_Printf("file name:%s\n", filename);
  //NX_Printf("load ");
  /* Load cassette */
  if ( InfoNES_Load ( szRomName ) == 0 )
  { 
    /* Set graphic content */
    //NX_Printf("load2 ");
    /* Load SRAM */
    if (LoadSRAM()) {
        return;
    }

    char title[32] = {0};
    strcpy(title, "infones - ");
    strcat(title, filename);
#ifdef USE_NX_WINDOW
    NX_WindowInit();

#ifdef USE_FULLSCREEN
    win = NX_WindowCreate(0, 0, 0, 0, NX_ColorRGB(0), NX_WINDOW_FLAG_FULLSCREEN_DESKTOP);
#else
    win = NX_WindowCreate(0, 0, NES_DISP_WIDTH, NES_DISP_HEIGHT, NX_ColorRGB(0), 0);
#endif
    if (!win)
    {
      return;
    }
    surface = win->connect.surface;
#ifdef USE_FULLSCREEN
    surfaceOrigin = NX_SurfaceAlloc(NES_DISP_WIDTH, NES_DISP_HEIGHT);
#endif
    NX_WindowShow(win);
#else
    /* open fb */
    fb_solt = NX_DeviceOpen("fb0", 0);
    if (fb_solt < 0) {
        NX_Printf("open fb device failed!\n");
        return;
    }
 
    if (NX_DeviceControl(fb_solt, NX_FRAMEBUFFER_CMD_GETINFO, &fbInfo) != NX_EOK)
    {
        NX_Printf("get fb info failed!\n");
        NX_SoltClose(fb_solt);
        return;
    }

    NX_Printf("fbinfo: bpp:%d, scanline:%d, x:%d, y:%d, base:%p\n", fbInfo.bitsPerPixel, fbInfo.bytesPerScanLine, fbInfo.xResolution, fbInfo.yResolution, fbInfo.phyBasePtr);
    
    if (fbInfo.bitsPerPixel != 32)
    {
        NX_Printf("fb bits per pixel %d not 32!\n", fbInfo.bitsPerPixel);
        NX_SoltClose(fb_solt);
        return;
    }
    
    int bytes_per_pixel = fbInfo.bitsPerPixel / 8;
    NX_Size fb_len = bytes_per_pixel * fbInfo.yResolution * fbInfo.xResolution; 
    fb_buf = NX_DeviceMap(fb_solt, fb_len, NX_PROT_READ | NX_PROT_WRITE);
    if (fb_buf == NX_NULL) {
        NX_Printf("device map failed! %s\n", NX_ErrorToString(NX_ErrorGet()));
        NX_SoltClose(fb_solt);
        return NX_EPERM;
    }

    key_solt = NX_DeviceOpen("keyboard0", 0);
    if (key_solt < 0) {
        NX_Printf("open keyboard device failed!\n");
        return;
    }

    /* get leds state */
    NX_U32 leds = 0;
    NX_DeviceControl(key_solt, NX_INPUT_EVENT_CMD_GETLED, &leds);
    NX_Printf("keyboard leds:%x\n", leds);
#endif
    NX_Printf("[infones] init ok, enter nes main\n");

    print_keymap();

    /* quit window by my handler */
    NX_WindowSetQuit(exit_application);

    InfoNES_Main();
  } else {
      NX_Printf("[infones] InfoNES_Load failed!\n");
      return;
  }
}

#ifdef USE_NX_WINDOW
static void win_key_press(int keycode)
{
    switch (keycode) {
#else
static void win_key_press(NX_InputEvent *event)
{
    switch (event->code) {
#endif
    case JOYSTICK_RIGHT:
        dwKeyPad1 |= (1 << 7);
        break;
    case JOYSTICK_LEFT:
    
        dwKeyPad1 |= (1 << 6);
        break;
    case JOYSTICK_DOWN:
    
        dwKeyPad1 |= (1 << 5);
        break;
    case JOYSTICK_UP:
    
        dwKeyPad1 |= (1 << 4);
        break;
    case JOYSTICK_START:
    
        dwKeyPad1 |= (1 << 3);
        break;			/* Start */
    case JOYSTICK_SELECT:
        dwKeyPad1 |= (1 << 2);
        break;			/* Select */
    case JOYSTICK_A:
    case JOYSTICK_a:
        dwKeyPad1 |= (1 << 1);
        break;			/* 'A' */
    case JOYSTICK_B:
    case JOYSTICK_b:
        dwKeyPad1 |= (1 << 0);
        break;			/* 'B' */
    case JOYSTICK_CUT:
    case JOYSTICK_cut:
        /* 切换剪裁 */
        PPU_UpDown_Clip = (PPU_UpDown_Clip ? 0 : 1);
        break;
    case JOYSTICK_SAVE:
    case JOYSTICK_save:
        SaveSRAM();
        break;

    default:
        break;
    }
}

#ifdef USE_NX_WINDOW
static void win_key_release(int keycode)
{
    switch (keycode) {
#else
static void win_key_release(NX_InputEvent *event)
{
    switch (event->code) {
#endif
    case JOYSTICK_RIGHT:
        dwKeyPad1 &= ~(1 << 7);
        break;
    case JOYSTICK_LEFT:
        dwKeyPad1 &= ~(1 << 6);
        break;
    case JOYSTICK_DOWN:
        dwKeyPad1 &= ~(1 << 5);
        break;
    case JOYSTICK_UP:
        dwKeyPad1 &= ~(1 << 4);
        break;
    case JOYSTICK_START:
        dwKeyPad1 &= ~(1 << 3);
        break;			/* Start */
    case JOYSTICK_SELECT:
        dwKeyPad1 &= ~(1 << 2);
        break;			/* Select */
    case JOYSTICK_A:
    case JOYSTICK_a:
        dwKeyPad1 &= ~(1 << 1);
        break;			/* 'A' */
    case JOYSTICK_B:
    case JOYSTICK_b:
        dwKeyPad1 &= ~(1 << 0);
        break;			/* 'B' */
    case JOYSTICK_HOME:
        exit_application();
        break;
    default:
        break;
    }		
}

/*===================================================================*/
/*                                                                   */
/*           LoadSRAM() : Load a SRAM                                */
/*                                                                   */
/*===================================================================*/
int LoadSRAM()
{
/*
 *  Load a SRAM
 *
 *  Return values
 *     0 : Normally
 *    -1 : SRAM data couldn't be NX_FileRead
 */

  int fd = -1;
  unsigned char pSrcBuf[ SRAM_SIZE ];
  unsigned char chData;
  unsigned char chTag;
  int nRunLen;
  int nDecoded;
  int nDecLen;
  int nIdx;

  // It doesn't need to save it
  nSRAM_SaveFlag = 0;

  // It is finished if the ROM doesn't have SRAM
  if ( !ROM_SRAM )
    return 0;

  // There is necessity to save it
  nSRAM_SaveFlag = 1;

  // The preparation of the SRAM file name
  strcpy( szSaveName, szRomName );
  strcpy( strrchr( szSaveName, '.' ) + 1, "srm" );

  /*-------------------------------------------------------------------*/
  /*  Read a SRAM data                                                 */
  /*-------------------------------------------------------------------*/

  // Open SRAM file
  NX_Error err;
  fd = NX_FileOpen( szSaveName, NX_FILE_RDONLY, 0);
  if ( fd == -1 ) {
      NX_Printf("[infones] open file %s failed!\n", szSaveName);
    return -1;

  }
    
  // Read SRAM data
  NX_FileRead( fd, pSrcBuf, SRAM_SIZE);

  // Close SRAM file
  NX_FileClose( fd );

  /*-------------------------------------------------------------------*/
  /*  Extract a SRAM data                                              */
  /*-------------------------------------------------------------------*/

  nDecoded = 0;
  nDecLen = 0;

  chTag = pSrcBuf[ nDecoded++ ];

  while ( nDecLen < 8192 )
  {
    chData = pSrcBuf[ nDecoded++ ];

    if ( chData == chTag )
    {
      chData = pSrcBuf[ nDecoded++ ];
      nRunLen = pSrcBuf[ nDecoded++ ];
      for ( nIdx = 0; nIdx < nRunLen + 1; ++nIdx )
      {
        SRAM[ nDecLen++ ] = chData;
      }
    }
    else
    {
      SRAM[ nDecLen++ ] = chData;
    }
  }

  // Successful
  return 0;
}

/*===================================================================*/
/*                                                                   */
/*           SaveSRAM() : Save a SRAM                                */
/*                                                                   */
/*===================================================================*/
int SaveSRAM()
{
/*
 *  Save a SRAM
 *
 *  Return values
 *     0 : Normally
 *    -1 : SRAM data couldn't be written
 */

  // Successful
  return 0;
}

/*===================================================================*/
/*                                                                   */
/*                  InfoNES_Menu() : Menu screen                     */
/*                                                                   */
/*===================================================================*/
int InfoNES_Menu()
{
/*
 *  Menu screen
 *
 *  Return values
 *     0 : Normally
 *    -1 : Exit InfoNES
 */

  /* Nothing to do here */
  return 0;
}

/*===================================================================*/
/*                                                                   */
/*               InfoNES_ReadRom() : Read ROM image file             */
/*                                                                   */
/*===================================================================*/
int InfoNES_ReadRom( const char *pszFileName )
{
/*
 *  Read ROM image file
 *
 *  Parameters
 *    const char *pszFileName          (Read)
 *
 *  Return values
 *     0 : Normally
 *    -1 : Error
 */

  int fd;
  NX_Error err;

  /* Open ROM file */
  fd = NX_FileOpen( pszFileName, NX_FILE_RDONLY, 0);
  if ( fd == -1 ) {
      NX_Printf("[infones] open file %s failed!\n", pszFileName);
    return -1;
  }

  /* Read ROM Header */
  NX_FileRead( fd, &NesHeader, sizeof NesHeader);
  if ( memcmp( NesHeader.byID, "NES\x1a", 4 ) != 0 )
  {
      NX_Printf("[infones ] not a NES file!\n");
    /* not .nes file */
    NX_FileClose( fd );
    return -1;
  }

  /* Clear SRAM */
  memset( SRAM, 0, SRAM_SIZE );

  /* If trainer presents Read Triner at 0x7000-0x71ff */
  if ( NesHeader.byInfo1 & 4 )
  {
    NX_FileRead( fd, &SRAM[ 0x1000 ], 512);
  }

  /* Allocate Memory for ROM Image */
  ROM = (BYTE *)NX_MemAlloc( NesHeader.byRomSize * 0x4000 );
    if (ROM == NULL) {
        NX_Printf("[infones ] NX_MemAlloc failed!\n");
        NX_FileClose( fd );
        return -1;
    }
  /* Read ROM Image */
  NX_FileRead( fd, ROM, 0x4000*NesHeader.byRomSize);

  if ( NesHeader.byVRomSize > 0 )
  {
    /* Allocate Memory for VROM Image */
    VROM = (BYTE *)NX_MemAlloc( NesHeader.byVRomSize * 0x2000 );
    if (VROM == NULL) {
        NX_Printf("[infones ] NX_MemAlloc failed!\n");
        NX_FileClose( fd );
        return -1;
    }
    /* Read VROM Image */
    NX_FileRead( fd, VROM, 0x2000*NesHeader.byVRomSize);
  }

  /* File NX_FileClose */
  NX_FileClose( fd );

  /* Successful */
  return 0;
}

/*===================================================================*/
/*                                                                   */
/*           InfoNES_ReleaseRom() : Release a memory for ROM         */
/*                                                                   */
/*===================================================================*/
void InfoNES_ReleaseRom()
{
/*
 *  Release a memory for ROM
 *
 */
//NX_Printf("release ");
  if ( ROM )
  {
    NX_MemFree( ROM );
    ROM = NULL;
  }

  if ( VROM )
  {
    NX_MemFree( VROM );
    VROM = NULL;
  }
}

/*===================================================================*/
/*                                                                   */
/*             InfoNES_MemoryCopy() : memcpy                         */
/*                                                                   */
/*===================================================================*/
void *InfoNES_MemoryCopy( void *dest, const void *src, int count )
{
/*
 *  memcpy
 *
 *  Parameters
 *    void *dest                       (Write)
 *      Points to the starting address of the copied block's destination
 *
 *    const void *src                  (Read)
 *      Points to the starting address of the block of memory to copy
 *
 *    int count                        (Read)
 *      Specifies the size, in bytes, of the block of memory to copy
 *
 *  Return values
 *    Pointer of destination
 */

  memcpy( dest, src, count );
  return dest;
}

/*===================================================================*/
/*                                                                   */
/*             InfoNES_MemorySet() : memset                          */
/*                                                                   */
/*===================================================================*/
void *InfoNES_MemorySet( void *dest, int c, int count )
{
/*
 *  memset
 *
 *  Parameters
 *    void *dest                       (Write)
 *      Points to the starting address of the block of memory to fill
 *
 *    int c                            (Read)
 *      Specifies the byte value with which to fill the memory block
 *
 *    int count                        (Read)
 *      Specifies the size, in bytes, of the block of memory to fill
 *
 *  Return values
 *    Pointer of destination
 */

  memset( dest, c, count);  
  return dest;
}

void DelayMS(NX_U32 ms)
{
#if 0
    NX_ThreadSleep(ms);
#else
    NX_TimeVal start = NX_ClockGetMillisecond();
    while (NX_ClockGetMillisecond() - start < ms)
    {
    }
#endif
}

static void Infones_FPS(void)
{
  static NX_TimeVal tvStart = 0;
  static int fps = 0;

  if (!tvStart)
  {
    tvStart = NX_ClockGetMillisecond();
  }

  fps++;

  if (NX_ClockGetMillisecond() - tvStart >= 1000)
  {    
    tvStart = NX_ClockGetMillisecond();
    NX_Printf("Infones: fps:%d\n", fps);
    fps = 0;
  }
}

/*===================================================================*/
/*                                                                   */
/*      InfoNES_LoadFrame() :                                        */
/*           Transfer the contents of work frame on the screen       */
/*                                                                   */
/*===================================================================*/
void InfoNES_LoadFrame()
{
#ifdef USE_NX_WINDOW
  NX_Surface *surf;

#ifdef USE_FULLSCREEN
  surf = surfaceOrigin;
#else
  surf = surface;
#endif

  DWORD *win_buf = (DWORD *)surf->pixels;
  // Exchange 16-bit to 24-bit
  for (register int y = 0; y < NES_DISP_HEIGHT; y++)
  {
    WORD *p = &WorkFrame[(y << 8)];
    DWORD *q = &win_buf[(y * surf->width)];
    for (register int x = 0; x < NES_DISP_WIDTH; x++)
    {
      WORD wColor = p[x];
      DWORD *dColor = &q[x];
      *dColor = ((wColor & 0x7c00) << 9) | ((wColor & 0x03e0) << 6) | ((wColor & 0x001f) << 3) | (0xff << 24);
    }
  }

#ifdef USE_FULLSCREEN
  NX_SurfaceFullscreen(surface, surf, 1);
#endif
  NX_WindowUpdate(win);

#else
    // 先绘制到临时surface
   DWORD *fbFrame = (DWORD *)fb_buf;
  // Exchange 16-bit to 24-bit
  for (register int y = 0; y < NES_DISP_HEIGHT; y++)
  {
    WORD *p = &WorkFrame[(y << 8)];
    DWORD *q = &fbFrame[(y * fbInfo.xResolution)];
    for (register int x = 0; x < NES_DISP_WIDTH; x++)
    {
      WORD wColor = p[x];
      DWORD *dColor = &q[x];
      *dColor = ((wColor & 0x7c00) << 9) | ((wColor & 0x03e0) << 6) | ((wColor & 0x001f) << 3) | (0xff << 24);
    }
  }
#endif
  //Infones_FPS();
#if !defined(USE_FULLSCREEN) || defined(USE_HW_ACCEL)
  DelayMS(20);
#endif
}

static int NX_PollEvent(void)
{
#ifdef USE_NX_WINDOW
    NX_WindowEvent e;
    if (NX_WindowPollEvent(win, &e) == NX_EOK)
    {
      switch (e.type)
      {
      case NX_KEYDOWN_EVENT:
        win_key_press(e.keydown_event.code);
        break;
      case NX_KEYUP_EVENT:
        win_key_release(e.keyup_event.code);
        break;
      default:
        break;
      }
    }
#else
    NX_InputEvent e;
    if (NX_DeviceRead(key_solt, &e, 0, sizeof(e)) > 0)
    {
        if (e.type == NX_EV_KEY)
        {
            if (e.value == 1)
            {
                win_key_press(&e);
            }
            else
            {
                win_key_release(&e);
            }
        }
    }
#endif
    return 1;
}

int PollEvent(void)
{
    if (NX_PollEvent())
        return 0;
    else
        exit_application();
    return -1; 
}

/*===================================================================*/
/*                                                                   */
/*             InfoNES_PadState() : Get a joypad state               */
/*                                                                   */
/*===================================================================*/
void InfoNES_PadState( DWORD *pdwKeyPad1, DWORD *pdwPad2, DWORD *pdwSystem )
{
/*
 *  Get a joypad state
 *
 *  Parameters
 *    DWORD *pdwKeyPad1                   (Write)
 *      Joypad 1 State
 *
 *    DWORD *pdwPad2                   (Write)
 *      Joypad 2 State
 *
 *    DWORD *pdwSystem                 (Write)
 *      Input for InfoNES
 *
 */
    PollEvent();
  /* Transfer joypad state */
  *pdwKeyPad1   = dwKeyPad1;
  *pdwPad2   = dwKeyPad2;
  *pdwSystem = dwKeySystem;
}

/*===================================================================*/
/*                                                                   */
/*        InfoNES_SoundInit() : Sound Emulation Initialize           */
/*                                                                   */
/*===================================================================*/
void InfoNES_SoundInit( void ) 
{
#ifdef USE_SOUND
    sound_fd = 0;
#endif
}

/*===================================================================*/
/*                                                                   */
/*        InfoNES_SoundOpen() : Sound Open                           */
/*                                                                   */
/*===================================================================*/
int InfoNES_SoundOpen( int samples_per_sync, int sample_rate ) 
{
    
#ifdef USE_SOUND
    NX_Printf("InfoNES_SoundOpen: samples_per_sync=%d, sample_rate=%d\n", samples_per_sync, sample_rate);
    sound_fd = NX_DeviceOpen(SOUND_DEVICE, 0);
    if (sound_fd < 0) {
        sound_fd = -1;
        return 0;
    }
    NX_U32 val;

    val = NX_AUDIO_FTM_S16_LE;
    NX_DeviceControl(sound_fd, NX_AUDIO_SET_FMT, &val);
    val = 44100;
    NX_DeviceControl(sound_fd, NX_AUDIO_SET_SPEED, &val);
    val = 2;
    NX_DeviceControl(sound_fd, NX_AUDIO_SET_CHANNELS, &val);
#endif
  /* Successful */
  return 1;
}

/*===================================================================*/
/*                                                                   */
/*        InfoNES_SoundClose() : Sound Close                         */
/*                                                                   */
/*===================================================================*/
void InfoNES_SoundClose( void ) 
{

#ifdef USE_SOUND
  if (sound_fd != -1) {
      NX_DeviceClose(sound_fd);
    }
#endif
}

/*===================================================================*/
/*                                                                   */
/*            InfoNES_SoundOutput() : Sound Output 5 Waves           */           
/*                                                                   */
/*===================================================================*/
void InfoNES_SoundOutput( int samples, BYTE *wave1, BYTE *wave2, BYTE *wave3, BYTE *wave4, BYTE *wave5 )
{

#ifdef USE_SOUND
  if (sound_fd != -1)
  {
    for (int i = 0; i < samples; i++)
    {
      final_wave[i * 2 + 1] = final_wave[i * 2] = (wave1[i] + wave2[i] + wave3[i] + wave4[i] + wave5[i]) * 50;
    }

    if (NX_DeviceWrite(sound_fd, final_wave, 0, samples * 4) < samples * 4)
    {
      NX_Printf("wrote less than 1024 bytes\n");
    }
  }
#endif /* USE_SOUND */
}

/*===================================================================*/
/*                                                                   */
/*            InfoNES_Wait() : Wait Emulation if required            */
/*                                                                   */
/*===================================================================*/
void InfoNES_Wait() 
{
}

/*===================================================================*/
/*                                                                   */
/*            InfoNES_MessageBox() : Print System Message            */
/*                                                                   */
/*===================================================================*/
void InfoNES_MessageBox( char *pszMsg, ... )
{
  char pszErr[ 1024 ];

	va_list args = (va_list)((char*)(&pszMsg) + 4); /*4是参数fmt所占堆栈中的大小*/
	//vsprintf(pszErr, pszMsg, args);

  //NX_Printf("%s", pszErr);
}

#include <InfoNES.c>
#include <InfoNES_Mapper.c>
#include <InfoNES_pAPU.c>
#include <K6502.c>
